package com.lagou.filter;

import com.lagou.pojo.TimeRecord;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;

/**
 * @author czq
 * @date 2020/9/2 22:29
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    /**
     * 每个方法对应的耗时记录
     */
    private static Map<String, List<TimeRecord>> map = new HashMap<>();

    /**
     * 每个方法上一次打印时间
     */
    private static Map<String, Long> PRE_PRINT_TIME_MAP = new HashMap<>();

    /**
     * 打印时间间隔
     */
    private static final int PRINT_TIME = 5000;

    /**
     * 统计时间间隔
     */
    private static final int CALCULATE_TIME = 60000;

    /**
     * TP90
     */
    private static final double TP_90 = 0.9;

    /**
     * TP99
     */
    private static final double TP_99 = 0.99;

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        // 计算请求时间
        long start = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long end = System.currentTimeMillis();
        int difference = (int) (end - start);

        TimeRecord timeRecord = new TimeRecord();
        timeRecord.setDifference(difference);
        timeRecord.setCurrentTime(end);

        // 存储请求记录
        String methodName = invocation.getMethodName();
        if (map.containsKey(methodName)) {
            List<TimeRecord> list = map.get(methodName);
            list.add(timeRecord);
        } else {
            List<TimeRecord> list = new ArrayList<>();
            list.add(timeRecord);
            map.put(methodName, list);
        }

        // 判断上一次打印时间是否超过5秒
        Long prePrintTime = PRE_PRINT_TIME_MAP.get(methodName);
        if (prePrintTime == null) {
            prePrintTime = end;
            PRE_PRINT_TIME_MAP.put(methodName, end);
        }
        if ((end - prePrintTime) > PRINT_TIME) {
            System.out.println("方法" + methodName + "【TP90耗时：" + calculateTp(end, map.get(methodName), TP_90)
                    + "；TP99耗时：" + calculateTp(end, map.get(methodName), TP_99) + "】");
            // 更新上一次打印时间
            PRE_PRINT_TIME_MAP.put(methodName, end);
        }

        return result;
    }

    /**
     * 计算TP
     */
    private int calculateTp(long currentTime, List<TimeRecord> list, double type) {
        List<Integer> timeList = new ArrayList<>();
        for (TimeRecord timeRecord : list) {
            // 筛选一分钟内的请求耗时
            if ((currentTime - timeRecord.getCurrentTime()) <= CALCULATE_TIME) {
                timeList.add(timeRecord.getDifference());
            }
        }

        return calculate(timeList, type);
    }

    /**
     * 计算
     */
    private int calculate(List<Integer> list, double type) {
        Collections.sort(list);
        int index = (int) (list.size() * type - 1);
        return list.get(index);
    }
}
